package com.kot.backend.consumer;

import com.alibaba.fastjson2.JSONObject;
import com.kot.backend.consumer.utils.BotStep;
import com.kot.backend.consumer.utils.JwtAuthentication;
import com.kot.backend.consumer.utils.Tank;
import com.kot.backend.mapper.BotMapper;
import com.kot.backend.mapper.RecordMapper;
import com.kot.backend.mapper.UserMapper;
import com.kot.backend.pojo.Bot;
import com.kot.backend.pojo.Record;
import com.kot.backend.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
@ServerEndpoint("/websocket/{token}")  // 表明映射到前端哪个链接，这里也是用jwt验证身份，注意不要以'/'结尾
public class WebSocketServer {

    //与线程安全有关的哈希表（每个实例在线程里面，所以要线程安全），将userID映射到相应用户的WebSocketServer
    // 需要一个对所有websocket可见的全局变量(static实现，和对象无关，绑定在类上)，存储所有的链接
    public static ConcurrentHashMap<Integer, WebSocketServer> users = new ConcurrentHashMap<>();

    //每个链接用户都有一个线程，专门用来执行自己的ai代码
    public static ConcurrentHashMap<Integer, BotStep> BOTS = new ConcurrentHashMap<>();
    //当前链接请求的用户
    private User user;
    private static User temp_a=null ,temp_b=null ;
    private User opponent;//记录对手
    private User loser;//记录败者
    public String model;//地图信息，传给botrunningsystem;
    private Integer botId = -1;//记录ai编号，-1表示是真人玩家

    private Session session = null;//不同于http协议中的session，每个链接都是用session来维护

    private final static String addPlayerUrl = "http://127.0.0.1:3001/player/add/";
    private final static String removePlayerurl = "http://127.0.0.1:3001/player/remove/";

    /**********因为websocket不是spring组件，所以不能直接单例注入，需要在setter注入静态变量****/
    public static UserMapper userMapper = null;//用类名来访问
    public static RecordMapper recordMapper = null;//用类名来访问(注意，被其他类引用，一定要改成public，不然用不了)
    public static RestTemplate restTemplate = null;//springboot之间进行通信
    public static BotMapper botMapper = null;

    @Autowired
    public void setUserMapper(UserMapper userMapper){
        WebSocketServer.userMapper = userMapper;//用类名来访问
    }
    @Autowired
    public void setRecordMapper(RecordMapper recordMapper){
        WebSocketServer.recordMapper = recordMapper;//用类名来访问
    }
    @Autowired
    public void setRestTemplate(RestTemplate restTemplate) {
        WebSocketServer.restTemplate = restTemplate;
    }
    @Autowired
    public void setRecordMapper(BotMapper botMapper){
        WebSocketServer.botMapper = botMapper;//用类名来访问
    }


    /************注入在上面****************/




    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) throws IOException {
        this.session = session;
//        System.out.println("尝试连接");
        Integer userId = JwtAuthentication.getUserId(token);//调用工具类获取UserId
        this.user = userMapper.selectById(userId);
        if (this.user != null) {
            users.put(userId, this);//用户存在，就是合法建立连接
        } else {
            this.session.close();//用户不合法，直接关闭连接
        }
    }
    @OnClose
    public void onClose() {
        // 关闭链接，触发此函数
//        System.out.println("disconnected!");
        //断开连接的话要将user移除

        if(botId!=-1){//如果当前是ai在玩，就要停止ai继续获取下一步操作，要停止进程
            BOTS.get(user.getId()).setTag(false);
        }

        if (this.user != null) {
            users.remove((this.user.getId()));//从全局哈希表中删除该链接
        }
    }

public static void startGame(Integer aId,Integer bId){
    User a = userMapper.selectById(aId);
    User b = userMapper.selectById(bId);

    users.get(a.getId()).opponent=b;
    users.get(b.getId()).opponent=a;


    Integer aBotId = users.get(a.getId()).botId;
    Integer bBotId = users.get(b.getId()).botId;
    Bot bota = botMapper.selectById(aBotId);
    Bot botb = botMapper.selectById(bBotId);
    if(aBotId!=-1){
        BotStep botStep = new BotStep(a.getId(),b.getId(),bota,true,0);
        BOTS.put(a.getId(),botStep);
        botStep.start();
    }
    if(bBotId!=-1){
        BotStep botStep = new BotStep(b.getId(),a.getId(),botb,true,0);
        BOTS.put(b.getId(),botStep);
        botStep.start();
    }


//    Game game = new Game(a,b);//因为静态函数中不能使用非静态全局变量，所以新造俩静态变量应急
    WebSocketServer.temp_a = a;
    WebSocketServer.temp_b = b;

    JSONObject respA = new JSONObject();//匹配成功后，传给A的信息
    respA.put("event", "start-matching");//匹配成功的提示信息
    respA.put("opponent_id", b.getId());
    respA.put("opponent_kind","playerA");//对手坦克的种类
    respA.put("opponent_username", b.getUsername());//传对手信息
    respA.put("opponent_photo", b.getPhoto());

    //用users获取a的链接
    users.get(a.getId()).sendMessage(respA.toJSONString()); //用这个链接将信息传回给前端（在这里发送信息）

    JSONObject respB = new JSONObject();//匹配成功后，传给B的信息
    respB.put("event", "start-matching");
    respB.put("opponent_id", a.getId());
    respB.put("opponent_kind","playerB");//对手坦克的种类
    respB.put("opponent_username", a.getUsername());
    respB.put("opponent_photo", a.getPhoto());
    //用users获取b的链接
    users.get(b.getId()).sendMessage(respB.toJSONString()); //用这个链接将信息传回给前端（在这里发送信息）
}
    private void startMatching() {
        MultiValueMap<String, String> data = new LinkedMultiValueMap<>();//不同springboot传递消息时，必须用这个一键多值的哈希表
        data.add("user_id", this.user.getId().toString());//传id和分数
        data.add("rating", this.user.getRating().toString());
        restTemplate.postForObject(addPlayerUrl, data, String.class);//发送请求的函数，参数依次为：url,数据,匹配系统返回值类型
    }
    private void stopMatching() {
        MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
        data.add("user_id", this.user.getId().toString());
        restTemplate.postForObject(removePlayerurl, data, String.class);
    }

    public void move(Integer direction) {//只会传入01234;4表示停止

        JSONObject resp = new JSONObject();//A操作完后，把操作传递给B

        if(direction<4) {
            resp.put("event", "move");//确定信息类型
            resp.put("direction", direction);
            resp.put("status","move");

            if(this.opponent!=null){
//                System.out.println("我信息也发出去了");
                users.get(this.opponent.getId()).sendMessage(resp.toJSONString()); //用这个链接将信息传回给前端（在这里发送信息）
            }
        }else {
//            System.out.println("应该停了");
            resp.put("event", "stop");//确定信息类型
            resp.put("status","idle");
            resp.put("hh","xx");
            if(this.opponent!=null)users.get(this.opponent.getId()).sendMessage(resp.toJSONString()); //用这个链接将信息传回给前端（在这里发送信息）
        }
    }

    private void shoot() {

        JSONObject resp = new JSONObject();//A操作完后，把操作传递给B
        resp.put("event", "shoot");//确定信息类型
        if(this.opponent!=null)users.get(this.opponent.getId()).sendMessage(resp.toJSONString()); //用这个链接将信息传回给前端（在这里发送信息）
    }
    private void updateModel(String model) {

        JSONObject resp = new JSONObject();//A操作完后，把操作传递给B
        this.model = model;
        System.out.println("this.model="+this.model);
        System.out.println("WebSocketServer.model"+WebSocketServer.users.get(user.getId()).model);
        resp.put("event", "updateModel");//确定信息类型
        resp.put("model",model);
        if(this!=null && this.opponent!=null && users!=null && users.get(this.opponent.getId())!=null)
            users.get(this.opponent.getId()).sendMessage(resp.toJSONString()); //用这个链接将信息传回给前端（在这里发送信息）

    }
    private void updateRating(Integer isloser) {
        if(isloser==null)return ;
        if(botId!=-1){//如果当前是ai在玩，就要停止ai继续获取下一步操作，要停止进程
            BOTS.get(user.getId()).setTag(false);
        }
        Integer rating = WebSocketServer.userMapper.selectById(user.getId()).getRating();
        if(isloser==1){
            rating-=2;//输了减去2分
            this.loser = user;
        }else{
            rating+=2;
            this.loser = opponent;
        }
        user.setRating(rating);
        WebSocketServer.userMapper.updateById(user);

        Record record = new Record(
                null,//自增，不用传
                this.user.getId(),//对应aId，是本玩家
                this.opponent.getId(),
                this.loser.getId(),
                new Date()
        );
        WebSocketServer.recordMapper.insert(record);
    }

    private void sendText(String msg) {
        if(msg==null)return ;
        JSONObject resp = new JSONObject();
        resp.put("event", "sendText");//确定信息类型
        resp.put("msg",msg);
        if(this.opponent!=null)users.get(this.opponent.getId()).sendMessage(resp.toJSONString()); //用这个链接将信息传回给前端（在这里发送信息）
    }
    @OnMessage
    public void onMessage(String message, Session session) {//当作路由，根据从前端传来的message判断之后执行哪一个函数

        JSONObject data = JSONObject.parseObject(message);//字符串转换成json
        String event = data.getString("event");//从json中获取event，判断当前是在开始匹配，还是结束匹配
        if ("start-matching".equals(event)) {
            botId = data.getInteger("bot_id");
            startMatching();//把开始匹配的业务封装成一个函数
        } else if ("stop-matching".equals(event)) {
            stopMatching();//把结束匹配的业务封装成一个函数
        }else if ("move".equals(event)) {//前端向后端发送信息，事件类型是move
            move(data.getInteger("direction"));//调用move(方向)
        }else if ("shoot".equals(event)) {//前端向后端发送信息，事件类型是move
            shoot();
        }else if ("updateModel".equals(event)) {//前端向后端发送信息，事件类型是move
            updateModel(data.getString("model"));
        }else if ("updateRating".equals(event)) {//前端向后端发送信息，事件类型是move
            updateRating(data.getInteger("isloser"));
        }else if ("sendText".equals(event)) {//前端向后端发送信息，事件类型是move
            sendText(data.getString("msg"));
        }
    }




    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    public void sendMessage(String message){//自定义一个发送信息的函数（websocket都是异步通信过程，所以发信息要加锁）
        synchronized (this.session) { //加锁
            try {
                this.session.getBasicRemote().sendText(message); //从后端向当前链接发送信息
            } catch (IOException e) { //IO异常
                e.printStackTrace();
            }
        }
    }
}
